package org.offsync.util

import scala.collection.mutable.PriorityQueue
import org.offsync.rich._

sealed class CompletionException extends Exception

object CompletionException extends CompletionException

class ScheduledExecutor extends DisposableBase with Runnable {
  private val taskQueue = new PriorityQueue[ScheduledTask]()(ScheduledTask.priorityOrdering)
  
  def execute(task: => Unit): Unit = execute(Time.now)(task)

  def execute(time: Time)(task: => Unit): Unit = {
    val scheduledTask = time.schedule(task)
    lock.synchronized {
      taskQueue += scheduledTask
      lock.notifyAll()
    }
  }

  def process(): Boolean = {
    val readyTasks = lock.synchronized {
      taskQueue.dequeueWhile(_.expired).toList
    }
    
    try {
      for (readyTask <- readyTasks) {
        try {
          readyTask()
        } catch {
          case e if !e.isInstanceOf[CompletionException] => println("Unhandled exception in ScheduledExecutor")
        }
      }
      
      return true
    } catch {
      case e: CompletionException => {
        super.onDispose()
        return false
      }
    }
  }
  
  def stop(): Unit = {
    execute(Time.now) {
      throw new CompletionException()
    }
  }
  
  def run(): Unit = {
    try {
      while (true) {
        val readyTask = lock.synchronized {
          var done = false
          while (!done) {
            if (taskQueue.size == 0) {
              lock.wait()
            } else if (!taskQueue.max.expired) {
              lock.wait(taskQueue.max.scheduledTime.time)
            } else {
              done = true
            }
          }
          taskQueue.dequeue()
        }
        
        try {
          readyTask()
        } catch {
          case e if !e.isInstanceOf[CompletionException] => println("Unhandled exception in ScheduledExecutor")
        }
      }
    } catch {
      case e: CompletionException => super.onDispose()
    }
  }

  protected override def onDispose(): Unit = {
    val scheduledTask = Time.minValue.schedule { condition =>
      throw CompletionException
    }
    
    lock.synchronized {
      taskQueue += scheduledTask
      lock.notifyAll()
    }
  }
}
